/**
 * 词法分析：将公式字符串转换为token数组
 * @param {string} formula 运算公式
 * @returns {Array} token数组
 */
function tokenize(formula) {
  const tokens = [];
  // 确保匹配所有运算符、括号、数字、变量
  // 分组顺序：1=运算符/括号，2=数字，3=变量
  const regex = /\s*([+\-*/()])\s*|(\d+\.?\d*|\.\d+)|([a-zA-Z_][a-zA-Z0-9_]*)\s*/g;
  let match;

  while ((match = regex.exec(formula)) !== null) {
    const value = match[0].trim();
    if (!value) continue; // 跳过空匹配

    if (match[1]) {
      // 运算符或括号
      const type = ['(', ')'].includes(value) 
        ? (value === '(' ? 'LEFT_PAREN' : 'RIGHT_PAREN') 
        : 'OPERATOR';
      tokens.push({ type, value });
    } else if (match[2]) {
      // 数字（支持整数、小数、.开头的小数）
      tokens.push({ type: 'NUMBER', value: parseFloat(value) });
    } else if (match[3]) {
      // 变量（支持字母、数字、下划线，以字母/下划线开头）
      tokens.push({ type: 'VARIABLE', value });
    }
  }

  return tokens;
}

/**
 * 中缀表达式转后缀表达式（逆波兰式）
 * @param {Array} tokens 词法分析后的tokens
 * @returns {Array} 后缀表达式tokens
 */
function shuntingYard(tokens) {
  const output = [];
  const operators = [];
  // 运算符优先级：一元负号 > 乘除 > 加减
  const precedence = { 'NEG': 3, '*': 2, '/': 2, '+': 1, '-': 1 };

  for (let i = 0; i < tokens.length; i++) {
    const token = tokens[i];
    const prevToken = i > 0 ? tokens[i - 1] : null;

    // 处理一元负号（如 "-5" 或 "( -3)"）
    if (token.type === 'OPERATOR' && token.value === '-' && 
        (!prevToken || ['LEFT_PAREN', 'OPERATOR'].includes(prevToken.type))) {
      operators.push({ type: 'OPERATOR', value: 'NEG' }); // 用NEG标记一元负号
      continue;
    }

    switch (token.type) {
      case 'NUMBER':
      case 'VARIABLE':
        output.push(token);
        break;

      case 'LEFT_PAREN':
        operators.push(token);
        break;

      case 'RIGHT_PAREN':
        // 弹出运算符直到遇到左括号
        while (operators.length > 0 && operators[operators.length - 1].type !== 'LEFT_PAREN') {
          output.push(operators.pop());
        }
        operators.pop(); // 弹出左括号（不加入输出）
        break;

      case 'OPERATOR':
        // 弹出优先级更高或相等的运算符
        while (operators.length > 0 && operators[operators.length - 1].type !== 'LEFT_PAREN' &&
               precedence[operators[operators.length - 1].value] >= precedence[token.value]) {
          output.push(operators.pop());
        }
        operators.push(token);
        break;
    }
  }

  // 弹出剩余运算符
  while (operators.length > 0) {
    output.push(operators.pop());
  }

  return output;
}

/**
 * 计算后缀表达式
 * @param {Array} postfixTokens 后缀表达式tokens
 * @param {Object} data 变量值映射
 * @returns {number} 计算结果
 */
function evaluatePostfix(postfixTokens, data) {
  const stack = [];

  for (const token of postfixTokens) {
    switch (token.type) {
      case 'NUMBER':
        stack.push(token.value);
        break;

      case 'VARIABLE':
        if (data[token.value] === undefined) {
          throw new Error(`Undefined variable: ${token.value}`);
        }
        stack.push(Number(data[token.value])); // 确保是数字类型
        break;

      case 'OPERATOR':
        if (token.value === 'NEG') {
          // 一元负号：弹出一个元素取反
          const a = stack.pop();
          if (a === undefined) throw new Error('Invalid expression (NEG)');
          stack.push(-a);
        } else {
          // 二元运算符：弹出两个元素计算
          const b = stack.pop();
          const a = stack.pop();
          if (a === undefined || b === undefined) throw new Error('Invalid expression (binary)');

          switch (token.value) {
            case '+': stack.push(a + b); break;
            case '-': stack.push(a - b); break;
            case '*': stack.push(a * b); break;
            case '/': 
              if (b === 0) throw new Error('Division by zero');
              stack.push(a / b); 
              break;
            default: throw new Error(`Unknown operator: ${token.value}`);
          }
        }
        break;
    }
  }

  if (stack.length !== 1) {
    throw new Error(`Invalid expression: stack has ${stack.length} elements (expected 1)`);
  }

  return stack[0];
}

/**
 * 计算入口函数
 * @param {Object} options 包含公式和变量的配置
 * @returns {number} 计算结果（处理精度后）
 */
function calculate({ formula, data = {} }) {
  try {
    const tokens = tokenize(formula);
    const postfixTokens = shuntingYard(tokens);
    const result = evaluatePostfix(postfixTokens, data);

    // 处理浮点数精度（保留10位小数，避免科学计数法）
    return Number(parseFloat(result).toFixed(10));
  } catch (error) {
    throw new Error(`Calculation failed: ${error.message}`);
  }
}

module.exports = { calculate };